package com.msh.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.msh.common.VMSystem;
import com.msh.config.TopicConfig;
import com.msh.contract.SupplyCfg;
import com.msh.contract.SupplyChannel;
import com.msh.contract.TaskCompleteContract;
import com.msh.dao.TaskDao;
import com.msh.dto.UserDto;
import com.msh.emq.MqttProducer;
import com.msh.entity.TaskDetailsEntity;
import com.msh.entity.TaskEntity;
import com.msh.entity.TaskStatusTypeEntity;
import com.msh.exception.LogicException;
import com.msh.feignService.UserService;
import com.msh.feignService.VMService;
import com.msh.http.viewModel.CancelTaskViewModel;
import com.msh.http.viewModel.TaskReportInfo;
import com.msh.http.viewModel.TaskViewModel;
import com.msh.service.TaskDetailsService;
import com.msh.service.TaskService;
import com.msh.service.TaskStatusTypeService;
import com.msh.utils.JsonUtil;
import com.msh.viewmodel.Pager;
import com.msh.viewmodel.UserWork;
import com.msh.viewmodel.VendingMachineViewModel;
import io.sentry.event.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

@Service
@Slf4j
public class TaskServiceImpl extends ServiceImpl<TaskDao,TaskEntity> implements TaskService{

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Autowired
    private TaskDetailsService taskDetailsService;

    @Autowired
    private VMService vmService;

    @Autowired
    private TaskStatusTypeService statusTypeService;

    @Autowired
    private UserService userService;

    @Autowired
    private MqttProducer mqttProducer;

    @Override
    public boolean accept(long id,TaskEntity task) {
        //TaskEntity task = this.getById(id);//查询工单
        if (task.getTaskStatus() != VMSystem.TASK_STATUS_CREATE) {
            throw new LogicException("工单状态不是待处理");
        }
        task.setTaskStatus(VMSystem.TASK_STATUS_PROGRESS);//修改工单状态为进行
        return this.updateById(task);
    }

    @Override
    public boolean cancelTask(Long id, CancelTaskViewModel cancelVM,TaskEntity task) {
//        TaskEntity task = this.getById(id);//查询工单
        if (task.getTaskStatus() == VMSystem.TASK_STATUS_FINISH || task.getTaskStatus() == VMSystem.TASK_STATUS_CANCEL) {
            throw new LogicException("工单状态不是待处理");
        }
        task.setTaskStatus(VMSystem.TASK_STATUS_CANCEL);
        task.setDesc(cancelVM.getDesc());
        //工单量分值-1
        updateTaskZset(task,-1);
        return this.updateById(task);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class},noRollbackFor = {LogicException.class})
    public boolean createTask(TaskViewModel taskViewModel) throws LogicException {
        checkCreateTask(taskViewModel.getInnerCode(),taskViewModel.getProductType());//验证
        if(hasTask(taskViewModel.getInnerCode(),taskViewModel.getProductType())) {
            throw new LogicException("该机器:"+taskViewModel.getInnerCode()+"有未完成的同类型工单");
        }

        //向工单表插入一条记录
        TaskEntity taskEntity = new TaskEntity();
        BeanUtils.copyProperties(taskViewModel,taskEntity);
        taskEntity.setTaskCode(this.generateTaskCode());
        taskEntity.setTaskStatus(VMSystem.TASK_STATUS_CREATE);
//        taskEntity.setCreateType(taskViewModel.getCreateType());
//        taskEntity.setDesc(taskViewModel.getDesc());
        taskEntity.setProductTypeId(taskViewModel.getProductType());
        String userName = userService.getUser(taskViewModel.getAssignorId()).getUserName();
        taskEntity.setUserName(userName);
//        taskEntity.setInnerCode(taskViewModel.getInnerCode());
//        taskEntity.setAssignorId(taskViewModel.getAssignorId());
        //计划完成时间，已弃用
//        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd");
//        LocalDate date = LocalDate.parse(taskViewModel.getExpect(),df);
//        LocalDateTime dateTime = date.atStartOfDay();guoqi
//        taskEntity.setExpect(dateTime);

        //taskEntity.setUserId(taskViewModel.getUserId());
        VendingMachineViewModel vm = vmService.getVMInfo(taskViewModel.getInnerCode());
        taskEntity.setAddr(vm.getNodeAddr());
        taskEntity.setRegionId(  vm.getRegionId() );
        this.save(taskEntity);

        //如果是补货工单，向 工单明细表插入记录
        if(taskEntity.getProductTypeId() == VMSystem.TASK_TYPE_SUPPLY){
            taskViewModel.getDetails().forEach(d->{
                TaskDetailsEntity detailsEntity = new TaskDetailsEntity();
                BeanUtils.copyProperties(d,detailsEntity);
//                detailsEntity.setChannelCode(d.getChannelCode());
//                detailsEntity.setExpectCapacity(d.getExpectCapacity());
                detailsEntity.setTaskId(taskEntity.getTaskId());
//                detailsEntity.setSkuId(d.getSkuId());
//                detailsEntity.setSkuName(d.getSkuName());
//                detailsEntity.setSkuImage(d.getSkuImage());
                taskDetailsService.save(detailsEntity);
            });
        }
        //工单量分值+1
        updateTaskZset(taskEntity,1);
        return true;
    }
    /**
     * 更新工单统计量列表
     * @param taskEntity
     * @param score
     */
    private void updateTaskZset(TaskEntity taskEntity, int score) {
        String roleCode = "1003";
        if (taskEntity.getProductTypeId() == 2) {//运营工单
            roleCode = "1002";
        }
        String key = VMSystem.REGION_TASK_KEY_PREF
                + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))
                +"."+taskEntity.getRegionId() + "." + roleCode;
        redisTemplate.opsForZSet().incrementScore(key,taskEntity.getAssignorId(),score);
    }

    @Override
    public boolean completeTask(long id) {
        return completeTask(id,0d,0d,"");
    }

    @Override
    public boolean completeTask(long id, Double lat, Double lon, String addr) {
        TaskEntity taskEntity = this.getById(id);
        if (taskEntity.getTaskStatus() == VMSystem.TASK_STATUS_FINISH || taskEntity.getTaskStatus() == VMSystem.TASK_STATUS_CANCEL) {
            throw new LogicException("工单已经结束");
        }
        taskEntity.setTaskStatus(VMSystem.TASK_STATUS_FINISH);
        taskEntity.setAddr(addr);
        this.updateById(taskEntity);

        //如果是补货工单
        if (taskEntity.getProductTypeId() == VMSystem.TASK_TYPE_SUPPLY) {
            noticeVMServiceSupply(taskEntity);
        }
        //如果是运维工单
        if (taskEntity.getProductTypeId() == VMSystem.TASK_TYPE_DEPLOY
                || taskEntity.getProductTypeId() == VMSystem.TASK_TYPE_REVOKE) {
            noticeVMServiceStatus(lat, lon, taskEntity);
        }

        return true;
    }

    private void noticeVMServiceStatus(Double lat, Double lon, TaskEntity taskEntity) {
        //向消息队列发送消息，通知售货机更改状态
        //封装协议
        TaskCompleteContract taskCompleteContract = new TaskCompleteContract();
        taskCompleteContract.setInnerCode(taskEntity.getInnerCode());//售货机编号
        taskCompleteContract.setTaskType(taskEntity.getProductTypeId());//工单类型
        taskCompleteContract.setLat(lat);//纬度
        taskCompleteContract.setLon(lon);//经度
        //发送到emq
        try {
            mqttProducer.send(TopicConfig.COMPLETED_TASK_TOPIC,2,taskCompleteContract);
        } catch (JsonProcessingException e) {
            log.error("发送工单完成协议出错");
            throw new LogicException("发送工单完成协议出错");
        }
    }


    @Override
    public List<TaskStatusTypeEntity> getAllStatus() {
        QueryWrapper<TaskStatusTypeEntity> qw = new QueryWrapper<>();
        qw.lambda()
                .ge(TaskStatusTypeEntity::getStatusId,VMSystem.TASK_STATUS_CREATE);

        return statusTypeService.list(qw);
    }

    @Override
    public Pager<TaskEntity> search(Long pageIndex, Long pageSize, String innerCode, Integer userId, String taskCode,Integer status,Boolean isRepair,String start,String end) {
        Page<TaskEntity> page = new Page<>(pageIndex,pageSize);
        LambdaQueryWrapper<TaskEntity> qw = new LambdaQueryWrapper<>();
        if(!Strings.isNullOrEmpty(innerCode)){
            qw.eq(TaskEntity::getInnerCode,innerCode);
        }
        if(userId != null && userId > 0){
            qw.eq(TaskEntity::getAssignorId,userId);
        }
        if(!Strings.isNullOrEmpty(taskCode)){
            qw.like(TaskEntity::getTaskCode,taskCode);
        }
        if(status != null && status > 0){
            qw.eq(TaskEntity::getTaskStatus,status);
        }
        if(isRepair != null){
            if(isRepair){
                qw.ne(TaskEntity::getProductTypeId,VMSystem.TASK_TYPE_SUPPLY);
            }else {
                qw.eq(TaskEntity::getProductTypeId,VMSystem.TASK_TYPE_SUPPLY);
            }
        }
        if(!Strings.isNullOrEmpty(start) && !Strings.isNullOrEmpty(end)){
            qw
                    .ge(TaskEntity::getCreateTime,LocalDate.parse(start,DateTimeFormatter.ISO_LOCAL_DATE))
                    .le(TaskEntity::getCreateTime,LocalDate.parse(end,DateTimeFormatter.ISO_LOCAL_DATE));
        }
        //根据创建时间，最后更新时间倒序排序
        qw.orderByDesc(TaskEntity::getCreateTime);
        qw.orderByDesc(TaskEntity::getUpdateTime);
        return Pager.build(this.page(page,qw));
    }


    /**
     * 补货协议封装与下发
     * @param taskEntity
     */
    private void noticeVMServiceSupply(TaskEntity taskEntity){
        //根据工单id查询工单明细表
        QueryWrapper<TaskDetailsEntity> qw = new QueryWrapper<>();
        qw.lambda()
                .eq(TaskDetailsEntity::getTaskId,taskEntity.getTaskId());
        List<TaskDetailsEntity> details = taskDetailsService.list(qw);
        //构建协议内容
        SupplyCfg supplyCfg = new SupplyCfg();
        supplyCfg.setInnerCode(taskEntity.getInnerCode());//售货机编号
        List<SupplyChannel> supplyChannels = Lists.newArrayList();//补货数据
        details.forEach(d->{
            SupplyChannel channel = new SupplyChannel();
            channel.setChannelId(d.getChannelCode());
            channel.setCapacity(d.getExpectCapacity());
            supplyChannels.add(channel);
        });
        supplyCfg.setSupplyData(supplyChannels);

        //下发补货协议
        //发送到emq
        try {
            mqttProducer.send(TopicConfig.COMPLETED_TASK_TOPIC,2,supplyCfg);
        } catch (JsonProcessingException e) {
            log.error("发送工单完成协议出错");
            throw new LogicException("发送工单完成协议出错");
        }

    }

    /**
     * 同一台设备下是否存在未完成的工单
     * @param innerCode
     * @param productionType
     * @return
     */
    private boolean hasTask(String innerCode,int productionType){
        QueryWrapper<TaskEntity> qw = new QueryWrapper<>();
        qw.lambda()
                .select(TaskEntity::getTaskId)
                .eq(TaskEntity::getInnerCode,innerCode)
                .eq(TaskEntity::getProductTypeId,productionType)
                .lt(TaskEntity::getTaskStatus,VMSystem.TASK_STATUS_FINISH);

        return this.count(qw) > 0;
    }

    private void checkCreateTask(String innerCode,int productType) throws LogicException {
        VendingMachineViewModel vmInfo = vmService.getVMInfo(innerCode);//根据设备编号查询设备
        if(vmInfo == null) throw new LogicException("设备校验失败");
        if(productType == VMSystem.TASK_TYPE_DEPLOY  && vmInfo.getVmStatus() == VMSystem.VM_STATUS_RUNNING){
            throw new LogicException("该设备已在运营");
        }

        if(productType == VMSystem.TASK_TYPE_SUPPLY  && vmInfo.getVmStatus() != VMSystem.VM_STATUS_RUNNING){
            throw new LogicException("该设备不在运营状态");
        }

        if(productType == VMSystem.TASK_TYPE_REVOKE  && vmInfo.getVmStatus() != VMSystem.VM_STATUS_RUNNING){
            throw new LogicException("该设备不在运营状态");
        }
    }


    /**
     * 生成工单编码
     * @return
     */
    private String generateTaskCode(){
        String key = "msh.task.code."+LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
        Object obj = redisTemplate.opsForValue().get(key);
        if(obj == null){
            redisTemplate.opsForValue().set(key,1L, Duration.ofDays(1));
            //return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))  +   Strings.padStart("1",4,'0');
            return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))  +   "0001";
        }

        return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + Strings.padStart(Objects.requireNonNull(redisTemplate.opsForValue().increment(key, 1)).toString(),4,'0')  ;
    }

    @Override
    public Integer getLeastuser(Long regionId, Boolean isRepair) {
        String roleCode = "1002";
        if (isRepair) {
            roleCode = "1003";
        }
        String key = VMSystem.REGION_TASK_KEY_PREF
                + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))
                +"."+ regionId + "." + roleCode;
        Set<Object> set = redisTemplate.opsForZSet().range(key, 0, 1);
        if (set == null || set.isEmpty()) {
            throw new LogicException("该区域暂时没有相关人员");
        }
        /*UserDto user = null;
        try {
            user = JsonUtil.getByJson((String) set.stream().collect(Collectors.toList()).get(0), UserDto.class);
        } catch (IOException e) {
            log.error("用户信息序列化失败");
            e.printStackTrace();
        }*/
        return (Integer) set.stream().collect(Collectors.toList()).get(0);
    }

    @Override
    public List<TaskReportInfo> getTaskReportInfo(LocalDateTime start, LocalDateTime end) {
        //运营工单总数
        var supplyTotalFuture = CompletableFuture.supplyAsync(()->this.taskCount(start,end,false,null));
        //运维工单总数
        var repairTotalFuture = CompletableFuture.supplyAsync(()->this.taskCount(start,end,true,null));
        //完成的运营工单数
        var completedSupplyFuture = CompletableFuture.supplyAsync(()->this.taskCount(start,end,false,VMSystem.TASK_STATUS_FINISH));
        //完成的运维工单数
        var completedRepairFuture = CompletableFuture.supplyAsync(()->this.taskCount(start,end,true,VMSystem.TASK_STATUS_FINISH));
        //拒绝的运营工单
        var rejectSupplyFuture = CompletableFuture.supplyAsync(()->this.taskCount(start,end,false,VMSystem.TASK_STATUS_CANCEL));
        //拒绝的运维工单数
        var cancelRepairFuture = CompletableFuture.supplyAsync(()->this.taskCount(start,end,true,VMSystem.TASK_STATUS_CANCEL));;

        //运营人员数量
        var operatorCountFuture = CompletableFuture.supplyAsync(()->userService.getOperatorCount());
        //运维人员数量
        var repairCountFuture = CompletableFuture.supplyAsync(()->userService.getRepairerCount());

        CompletableFuture.allOf(supplyTotalFuture,
                repairCountFuture,
                completedRepairFuture,
                completedSupplyFuture,
                rejectSupplyFuture,
                cancelRepairFuture,
                operatorCountFuture,
                repairCountFuture).join();

        List<TaskReportInfo> result = new ArrayList<>();
        var supplyTaskInfo = new TaskReportInfo();//运营
        var repariTaskInfo = new TaskReportInfo();//运维

        try {
            //运营
            supplyTaskInfo.setTotal(supplyTotalFuture.get());
            supplyTaskInfo.setCancelTotal(rejectSupplyFuture.get());
            supplyTaskInfo.setCompletedTotal(completedSupplyFuture.get());
            supplyTaskInfo.setRepair(false);
            supplyTaskInfo.setWorkerCount(operatorCountFuture.get());
            result.add(supplyTaskInfo);

            //运维
            repariTaskInfo.setTotal(repairTotalFuture.get());
            repariTaskInfo.setCancelTotal(cancelRepairFuture.get());
            repariTaskInfo.setCompletedTotal(completedRepairFuture.get());
            repariTaskInfo.setRepair(true);
            repariTaskInfo.setWorkerCount(repairCountFuture.get());
            result.add(repariTaskInfo);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 统计工单数量
     * @param start
     * @param end
     * @param repair
     * @param taskStatus
     * @return
     */
    private int taskCount(LocalDateTime start, LocalDateTime end, Boolean repair, Integer taskStatus) {
        LambdaQueryWrapper<TaskEntity> qw = new LambdaQueryWrapper<>();
        qw.ge(TaskEntity::getUpdateTime,start)
                .le(TaskEntity::getUpdateTime,end);//事件单
        if (taskStatus != null) {
            qw.eq(TaskEntity::getTaskStatus,taskStatus);//工单状态
        }
        if (repair) {//如果是运维
            qw.ne(TaskEntity::getProductTypeId,VMSystem.TASK_TYPE_SUPPLY);
        }else {
            qw.eq(TaskEntity::getProductTypeId,VMSystem.TASK_TYPE_SUPPLY);
        }
        return this.count(qw);
    }

    @Override
    public UserWork getUserWork(Integer userId, LocalDateTime start, LocalDateTime end) {
        var userWork = new UserWork();
        userWork.setUserId(userId);

        //获取用户完成工单数
        CompletableFuture<Integer> workCountFuture = CompletableFuture.supplyAsync(() -> this.getCountByUserId(userId, VMSystem.TASK_STATUS_FINISH, start, end))
                .whenComplete((r, e) -> {
                    if (e != null) {
                        userWork.setWorkCount(0);
                        log.error("user work error", e);
                    } else userWork.setWorkCount(r);
                });
        //获取用户总工单数
        CompletableFuture<Integer> totalFuture = CompletableFuture.supplyAsync(()->this.getCountByUserId(userId,null,start,end))
                .whenComplete((r,e)->{
                    if (e != null) {
                        userWork.setTotal(0);
                        log.error("user work error",e);
                    }else userWork.setTotal(r);
                });
        //获取用户拒绝工单数
        CompletableFuture<Integer> cancelFuture = CompletableFuture.supplyAsync(()->this.getCountByUserId(userId,VMSystem.TASK_STATUS_CANCEL,start,end))
                .whenComplete((r,e)->{
                    if (e != null) {
                        userWork.setCancelCount(0);
                        log.error("user work error",e);
                    }else userWork.setCancelCount(r);
                });
        //获取用户进行中工单数
        CompletableFuture<Integer> progressFuture = CompletableFuture.supplyAsync(()->this.getCountByUserId(userId,VMSystem.TASK_STATUS_PROGRESS,start,end))
                .whenComplete((r,e)->{
                    if (e != null) {
                        userWork.setProgressTotal(0);
                        log.error("user work error",e);
                    }else userWork.setProgressTotal(r);
                });
        //并发执行
        CompletableFuture.allOf(workCountFuture,totalFuture,cancelFuture,progressFuture).join();

        return userWork;
    }

    /**
     * 根据用户id，工单状态，查询工单数
     * @param userId
     * @param taskStatus
     * @param start
     * @param end
     * @return
     */
    private Integer getCountByUserId(Integer userId, Integer taskStatus, LocalDateTime start, LocalDateTime end) {
        var qw = new LambdaQueryWrapper<TaskEntity>();
        qw.ge(TaskEntity::getUpdateTime, start)
                .le(TaskEntity::getUpdateTime, end);

        if (taskStatus != null) {
            qw.eq(TaskEntity::getTaskStatus, taskStatus);
        }
        if (userId != null) {
            qw.eq(TaskEntity::getAssignorId, userId);
        }
        return this.count(qw);
    }

    @Override
    public List<UserWork> getUserWorkTop10(LocalDate start, LocalDate end, Boolean isRepair, Long regionId) {
        QueryWrapper<TaskEntity> qw = new QueryWrapper<>();
        qw.select("count(user_id) as user_id,user_name")
                .lambda()
                .ge(TaskEntity::getUpdateTime,start)
                .le(TaskEntity::getUpdateTime,end)
                .eq(TaskEntity::getTaskStatus,VMSystem.TASK_STATUS_FINISH)
                .groupBy(TaskEntity::getUserName)
                .orderByDesc(TaskEntity::getUserId)
                .last("limit 10");
        if (regionId != null && regionId > 0) {
            qw.lambda().eq(TaskEntity::getRegionId, regionId);
        }
        if (isRepair) {
            qw.lambda().ne(TaskEntity::getUpdateTime,VMSystem.TASK_TYPE_SUPPLY);
        }else qw.lambda().eq(TaskEntity::getUpdateTime,VMSystem.TASK_TYPE_SUPPLY);

        return this.list(qw).stream().map(u->{
            UserWork userWork = new UserWork();
            userWork.setUserName(u.getUserName());
            userWork.setWorkCount(u.getUserId());
            return userWork;
        }).collect(Collectors.toList());
    }

    @Override
    public Integer getSupplyOrReparCount(Integer innerCode, LocalDate start, LocalDate end, Integer vmType, String nodeId, Integer createUserId,Boolean isRepair) {
        QueryWrapper<TaskEntity> qw = new QueryWrapper<>();
        qw.lambda().eq(TaskEntity::getInnerCode,innerCode)
                .ge(TaskEntity::getUpdateTime,start)
                .le(TaskEntity::getUpdateTime,end)
                .eq(TaskEntity::getProductTypeId,isRepair?VMSystem.TASK_TYPE_REPAIR:VMSystem.TASK_TYPE_SUPPLY)
                .eq(createUserId!=null,TaskEntity::getUserId,createUserId)
                .eq(TaskEntity::getTaskStatus,VMSystem.TASK_STATUS_FINISH);//工单状态已完成
        return this.count(qw);
    }
}
